#include "controls.h"

Controls::Controls(QVector3D Pos, float hAngle, float vAngle, float FOV, float Spd, float MouseSpd)
{
    CPosition= Pos;
    HorizontalAngle= hAngle;
    VerticalAngle= vAngle;
    InitialFoV= FOV;
    FoV= InitialFoV;
    Speed= Spd;
    MouseSpeed= MouseSpd;
    DUALRENDER= false;
    int RenderPass= SINGLE;

    // Resolucion por defecto
    width= 800;
    height= 600;
    AspectRatio= float(width)/float(height);
    EyeDistance= 0.035;
    ZeroParallax= 1.3;
    MousePos= QPoint (width/2, height/2);

    // Esto es para simular el movimiento con una velocidad correcta
    LastTime.start();
}

QMatrix4x4 Controls::getViewMatrix()
{
    return ViewMatrix;
}

QMatrix4x4 Controls::getProjectionMatrix()
{
    // Estereovision por anaglifo?
    if (DUALRENDER)
    {
        computeDualRender();
        if (RenderPass==LEFT)
        {
            ProjectionMatrix.translate(EyeDistance/2.0, 0.0, 0.0);
            RenderPass=RIGHT;
        }
        else if (RenderPass==RIGHT)
        {
            ProjectionMatrix.translate(-EyeDistance/2.0, 0.0, 0.0);
            RenderPass=LEFT;
        }
        else
            RenderPass=LEFT;
    }

    return ProjectionMatrix;
}

void Controls::SwitchDualRender(bool value)
{
    DUALRENDER= value;
    RenderPass=LEFT;
}

void Controls::SetZeroParallax(float val)
{
    if (val>0)
        ZeroParallax= val;
}

void Controls::SetEyeDistance(float val)
{
    if (val>=0)
        EyeDistance= val;
}

void Controls::setCDirection(QVector3D dir)
{
    CDirection.setX(dir.x());
    CDirection.setY(dir.y());
    CDirection.setZ(dir.z());
}

void Controls::computeDualRender()
{
    float top, bottom, left, right;
    float mFoV= FoV * PI / 180.0;

    top= 0.1 * tan(mFoV/2);
    bottom= -top;

    float a= float(width)/float(height) * tan(mFoV/2) * ZeroParallax;

    float Eyes= ZeroParallax * tan(mFoV/2) * 2;
    EyeDistance= 0.035*Eyes;

    float b= a - EyeDistance/2;
    float c= a + EyeDistance/2;

    if (RenderPass==LEFT)
    {
        left= -b * 0.1 / ZeroParallax;
        right= c * 0.1 / ZeroParallax;
    }
    else if (RenderPass==RIGHT)
    {
        left= -c * 0.1 / ZeroParallax;
        right= b * 0.1 / ZeroParallax;
    }

    ProjectionMatrix.setToIdentity();
    ProjectionMatrix.frustum(left, right, bottom, top, 0.1, 100.0);
}

void Controls::parentResized(int w, int h)
{
    width= w;
    height= h;
    AspectRatio= float(width)/float(height);
    MousePos= QPoint(w, h);

    //if (ortho)
    //ProjectionMatrix.ortho(-5.5, 5.5, -5.5, 5.5, 15.0, -15.0);
    //else
    //ProjectionMatrix.perspective(45, float(width) / float(height), 0.1, 200.0);

    ViewMatrix.setToIdentity();
    ViewMatrix.lookAt(CPosition, CPosition + CDirection, CUp);
}

void Controls::computeMatricesFromInputs(int keypressed, QPoint mpos)
{
    // Calculemos el tiempo transcurrido desde el ultimo frame con movimiento
    qint64 elapsedTime= LastTime.elapsed();
    float deltaTime= float (elapsedTime)/100.0;

    // Ahora veamos si hubo movimiento del mouse
    int xpos= 0; int ypos= 0;
    if (MousePos.x()>mpos.x())
        xpos= MousePos.x() - mpos.x();
    else if (MousePos.x()<mpos.x())
        xpos= MousePos.x() - mpos.x();

    if (MousePos.y()>mpos.y())
        ypos= MousePos.y() - mpos.y();
    else if (MousePos.y()<mpos.y())
        ypos= MousePos.y() - mpos.y();

    // Calculemos hacia donde apuntaba el mouse
    HorizontalAngle+= MouseSpeed * float(xpos)/float(width/2);
    VerticalAngle+= MouseSpeed * float(ypos)/float(height/2);
    //VerticalAngle= 0.0;
    //qDebug("HorizontalAngle: %f\nVerticalAngle: %f", HorizontalAngle, VerticalAngle);

    // Aplicamos un clamp para el angulo vertical (evita girar verticalmente 360º)
    if (VerticalAngle>1.570758) VerticalAngle=1.570758;
    else if (VerticalAngle<-1.570758) VerticalAngle=-1.570758;

    // Este vector permitira avanzar en la direccion correcta, se construye
    // convirtiendo la posicion esferica de la camara a una posicion cartesiana
    CDirection= QVector3D (cos(VerticalAngle)* sin(HorizontalAngle),
                            sin(VerticalAngle),
                            cos(VerticalAngle)* cos(HorizontalAngle));

    //qDebug("CDirection: %f %f %f", CDirection.x(), CDirection.y(), CDirection.z());

    // Ahora vamos a calcular el vector <right>
    CRight= QVector3D(sin(HorizontalAngle - 3.14f/2.0f), 0, cos(HorizontalAngle - 3.14f/2.0f));
    //qDebug("CRight: %f %f %f", CRight.x(), CRight.y(), CRight.z());

    // Luego el vector <up>
    CUp= QVector3D::crossProduct(CRight, CDirection);
    //qDebug("CUp: %f %f %f", CUp.x(), CUp.y(), CUp.z());

    // Esto es porque no queremos un salto abrupto en el movimiento
    if (deltaTime> 1.0)
        deltaTime= 1.0;

    if (keypressed==Qt::Key_W)
    {
        CPosition+= CDirection * deltaTime * Speed;
    }
    else if (keypressed==Qt::Key_S)
    {
        CPosition-= CDirection * deltaTime * Speed;
    }
    else if (keypressed==Qt::Key_A)
    {
        CPosition-= CRight * deltaTime * Speed;
    }
    else if (keypressed==Qt::Key_D)
    {
        CPosition+= CRight * deltaTime * Speed;
    }
    else if (keypressed==Qt::Key_Space)
    {
        CPosition+= QVector3D(0.0, 1.0, 0.0) * deltaTime * Speed;
    }
    else if (keypressed==Qt::Key_Shift)
    {
        CPosition-= QVector3D(0.0, 1.0, 0.0) * deltaTime * Speed;
    }
    //qDebug("CPosition: %f %f %f\n", CPosition.x(), CPosition.y(), CPosition.z());

    emit cameraMoves(CPosition);

    ViewMatrix.setToIdentity();
    ViewMatrix.lookAt(CPosition, CPosition + CDirection, CUp);

    if (DUALRENDER) // Estereovision por anaglifo, que pidan las matrices fuera de aqui
    {
        ProjectionMatrix.setToIdentity();
    }
    else
    {
        ProjectionMatrix.setToIdentity();
        ProjectionMatrix.perspective(45, float(width) / float(height), 0.1, 100.0);
    }

    LastTime.restart();
}
